package gateway.server.engine;

import gateway.api.engine.Chain;
import gateway.api.engine.Filter;
import gateway.api.engine.Outbound;
import gateway.api.message.Message;
import gateway.server.route.Route;
import gateway.server.route.RouteItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.ExecutorService;

/**
 * @author Jin Zheng
 * @since 1.0 2020-09-21
 */
public class DefaultChain implements Chain
{
	private static Logger logger = LoggerFactory.getLogger(DefaultChain.class);

	private final ExecutorService executor;
	private final Message message;
	private List<RouteItem<Filter>> filters;
	private RouteItem<Outbound> outbound;
	private State state = new State();

	public DefaultChain(ExecutorService executor, Route route, Message message)
	{
		this.executor = executor;
		this.message = message;
		this.filters = List.copyOf(route.getFilterList());
		this.outbound = route.getOutbound();
	}

	@Override
	public void doNext()
	{
		if (state.success && state.index < filters.size())
		{
			RouteItem<Filter> filter = filters.get(state.index++);
			if (filter.isAsync())
			{
				executor.execute(() -> this.doFilter(filter.getTarget()));
			}
			else
			{
				this.doFilter(filter.getTarget());
			}
			return;
		}
		if (state.success && outbound != null)
		{
			if (outbound.isAsync())
			{
				executor.execute(() -> this.doOutbound(outbound.getTarget()));
			}
			else
			{
				this.doOutbound(outbound.getTarget());
			}
		}
		logger.debug("Finish");
	}

	private void doFilter(Filter filter)
	{
		try
		{
			filter.handle(message, this);
		}
		catch (Exception e)
		{
			state.success = false;
			logger.error("Error, ", e);
		}
	}

	private void doOutbound(Outbound outbound)
	{
		try
		{
			outbound.send(message);
		}
		catch (Exception e)
		{
			state.success = false;
			logger.error("Error, ", e);
		}
	}

	class State
	{
		int index = 0;
		boolean success = true;
	}

}
